﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Expect.Core.CodeGen;
using Expect.Core;
using System.IO;
using EnvDTE;

namespace Expect.VisualStudio.Extensions.Services
{
    using EnvDTE80;

    public class FixtureResolution
    {
        private SelectionResolution _selection;

        private EnvDTE80.DTE2 _dte;

        public FixtureResolution(SelectionResolution selection)
        {
            _selection = selection;
            _dte = (EnvDTE80.DTE2)_selection.ExpectPackage.GetVSService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE));
            
            ExpectSpecParser parser = new ExpectSpecParser();
            _selection.Specification = parser.Execute(_selection.Context.FileName, _selection.Selection.FullText).Specification;
            _selection.Fixture = _selection.Specification.GetFixtureAtLine(_selection.Selection.Text, _selection.Selection.StartLine + 1);
            
            if (_selection.Fixture != null)
                _selection.ClickedRow = _selection.Fixture.GetRowAtLine(_selection.Selection.StartLine + 1);
        }

        public void Resolve()
        {
            if (_selection.Fixture == null) return;

            var fixtureClassName = _selection.NameProvider.ElementName(_selection.Fixture.Rows[0].NamesForFluent().ToArray());
            var specClassName = _selection.NameProvider.ElementName(_selection.Fixture.Parent.Name);


            if (!string.IsNullOrEmpty(specClassName))
            {
                var specmap = _selection.Context.Classes.FirstOrDefault(c => c.IsSpecification && c.ClassName == specClassName);
                if (specmap != null)
                    _selection.SpecItem = specmap;
            }

            var fixturemap = _selection.Context.GetFixtureClassFor(fixtureClassName);
            if (fixturemap != null)
            {
                var fixtureclass = fixturemap.Item.FileCodeModel.CodeElements.GetClass(fixturemap.FullName);
                
                _selection.FixtureResKind = FixtureResolutionKind.Class;
                _selection.FixtureMap = fixturemap;
                _selection.Target = fixturemap.Item;
                _selection.TargetPoint = fixtureclass.GetStartPoint(vsCMPart.vsCMPartBody);

                return;
            }

            //if (_selection.ClickedRow == null && string.IsNullOrEmpty(_selection.Selection.Text))
            //    return;

            if (HasFixtureMethodWithFixtureData(FixtureResolutionTarget.Specification, specClassName, _selection.Context.ContainerClassFullName))
                return;

            if (HasFixtureMethod(FixtureResolutionTarget.Specification, specClassName, _selection.Context.ContainerClassFullName))
                return;
        }        

        private bool HasFixtureMethodWithFixtureData(FixtureResolutionTarget target, params string[] classnames)
        {
            var methodname = _selection.NameProvider.ElementName(_selection.Fixture.Rows[0].NamesForFluent().ToArray());
            var methodargs = _selection.Fixture.Rows[0].ArgsForFluent().ToArray();

            foreach (var targetclass in classnames)
            {
                var map = _selection.Context.Classes.FirstOrDefault(c => c.FullName == targetclass || c.ClassName == targetclass);

                if (map != null)
                {
                    var codeclass = map.Item.FileCodeModel.CodeElements.GetClass(targetclass);

                    if (this.HasFixtureMethodWithFixtureData(codeclass, target, methodname, methodargs))
                        return true;
                }
            }

            return false;
        }

        private bool HasFixtureMethod(FixtureResolutionTarget target, params string[] classnames)
        {
            if (_selection.ClickedRow == null) return false;

            var methodname = _selection.NameProvider.ElementName(_selection.ClickedRow.NamesForFluent().ToArray());
            var methodargs = _selection.ClickedRow.ArgsForFluent().ToArray();

            foreach (var targetclass in classnames)
            {
                var map = _selection.Context.Classes.FirstOrDefault(c => c.FullName == targetclass || c.ClassName == targetclass);

                if (map != null)
                {
                    var codeclass = map.Item.FileCodeModel.CodeElements.GetClass(targetclass);

                    if (this.HasFixtureMethod(codeclass, target, methodname, methodargs))
                        return true;
                }
            }

            return false;
        }

        /*
        private bool ItemHasFixtureMethod(Dictionary<string, List<ProjectItem>> classes, string classname, FixtureResolutionTarget target)
        {
            if (!string.IsNullOrEmpty(classname) && classes.ContainsKey(classname))
            {
                var items = classes[classname];
                foreach (var item in items)
                {
                    if (item != null && item.FileCodeModel != null)
                    {
                        var codeclass = item.FileCodeModel.CodeElements.GetClass(classname);

                        if (codeclass == null) continue;

                        if (ProcessAsFixtureMethod(codeclass, target))
                            return true;

                        if (ProcessAsFixtureMethodWithFixtureData(codeclass, target))
                            return true;
                    }
                }
            }

            return false;
        }*/

        private bool HasFixtureMethodWithFixtureData(CodeClass codeclass, FixtureResolutionTarget target, string methodname, string[] methodargs)
        {
            var func = codeclass.HasFixtureMethodWithFixtureData(_selection.Context.NameProvider, methodname, methodargs);

            if (func != null)
            {
                _selection.FixtureResKind = FixtureResolutionKind.MethodWithFixtureData;
                _selection.FixtureResTarget = target;
                _selection.TargetPoint = func.StartPoint;
                _selection.Target = func.ProjectItem;
                return true;
            }

            return false;
        }

        private bool HasFixtureMethod(CodeClass codeclass, FixtureResolutionTarget target, string methodname, string[] methodargs)
        {
            var func = codeclass.HasFixtureMethod(_selection.Context.NameProvider, methodname, methodargs);

            if (func != null)
            {
                this._selection.FixtureResKind = FixtureResolutionKind.Method;
                this._selection.FixtureResTarget = target;
                _selection.TargetPoint = func.StartPoint;
                _selection.Target = func.ProjectItem;
                return true;
            }

            return false;
        }

        
    }
}
